package com.sl.ms.dispatch.mq;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.sl.ms.api.CourierFeign;
import com.sl.ms.base.api.common.MQFeign;
import com.sl.ms.work.api.PickupDispatchTaskFeign;
import com.sl.ms.work.domain.dto.CourierTaskCountDTO;
import com.sl.ms.work.domain.enums.pickupDispatchtask.PickupDispatchTaskType;
import com.sl.transport.common.constant.Constants;
import com.sl.transport.common.vo.CourierTaskMsg;
import com.sl.transport.common.vo.OrderMsg;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 订单业务消息，接收到新订单后，根据快递员的负载情况，分配快递员
 */
@Slf4j
@Component
public class OrderMQListener {

    @Resource
    private CourierFeign courierFeign;

    @Resource
    private PickupDispatchTaskFeign pickupDispatchTaskFeign;

    @Resource
    private MQFeign mqFeign;

    /**
     * 如果有多个快递员，需要查询快递员今日的取派件数，根据此数量进行计算
     * 计算的逻辑：优先分配取件任务少的，取件数相同的取第一个分配
     * <p>
     * 发送生成取件任务时需要计算时间差，如果小于2小时，实时发送；大于2小时，延时发送
     * 举例：
     * 1、现在10:30分，用户期望：11:00 ~ 12:00上门，实时发送
     * 2、现在10:30分，用户期望：13:00 ~ 14:00上门，延时发送，12点发送消息，延时1.5小时发送
     *
     * @param msg 消息内容
     */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = Constants.MQ.Queues.DISPATCH_ORDER_TO_PICKUP_DISPATCH_TASK),
            exchange = @Exchange(name = Constants.MQ.Exchanges.ORDER_DELAYED, type = ExchangeTypes.TOPIC, delayed = Constants.MQ.DELAYED),
            key = Constants.MQ.RoutingKeys.ORDER_CREATE
    ))
    public void listenOrderMsg(String msg) {
        //{"orderId":123, "agencyId": 8001, "taskType":1, "mark":"带包装", "longitude":116.111, "latitude":39.00, "created":1654224658728, "estimatedStartTime": 1654224658728}
        log.info("接收到订单的消息 >>> msg = {}", msg);
        // 1. 将json反序列化 OrderMsg对象
        OrderMsg orderMsg = JSONUtil.toBean(msg, OrderMsg.class);
        // 2. 获取网点id 获取经纬度
        Long agencyId = orderMsg.getAgencyId();
        Double longitude = orderMsg.getLongitude();
        Double latitude = orderMsg.getLatitude();
        // 3. 设置取派件快递员id=null 快递员可能为空
        Long selectedCourierId = null;
        // 4. 远程调用快递员微服务 ==> 查询满足作业范围且排班的快递员
        List<Long> courierIds = courierFeign.queryCourierIdListByCondition(agencyId, longitude, latitude,
                LocalDateTimeUtil.toEpochMilli(orderMsg.getEstimatedEndTime()));
        log.info("快递微服务查出的ids: {}", courierIds);
        // 5. 如果查询到快递员的id不为空 需要选择快递员
        if (CollUtil.isNotEmpty(courierIds)) {
            selectedCourierId = this.selectCourier(courierIds, orderMsg.getTaskType());
            log.info("根据当日任务选出的快递员id: {}", selectedCourierId);
        }
        // 6. 生成取件任务消息
        CourierTaskMsg courierTaskMsg = new CourierTaskMsg();
        courierTaskMsg.setCourierId(selectedCourierId);
        courierTaskMsg.setAgencyId(agencyId);
        courierTaskMsg.setTaskType(orderMsg.getTaskType());
        courierTaskMsg.setOrderId(orderMsg.getOrderId());
        courierTaskMsg.setMark(orderMsg.getMark());
        courierTaskMsg.setEstimatedEndTime(orderMsg.getEstimatedEndTime());
        courierTaskMsg.setInfo(orderMsg.getInfo());
        courierTaskMsg.setCreated(System.currentTimeMillis());
        // 7. 判断预计结束时间和当前时间的时间差 是否大于2小时
        long between = LocalDateTimeUtil.between(LocalDateTime.now(), orderMsg.getEstimatedEndTime(), ChronoUnit.MINUTES);
        Integer delayTime = Constants.MQ.DEFAULT_DELAY; //默认-1 表示不过期为实时任务
        //  如果大于2小时 需要计算延迟时间
        if (between > 2 && orderMsg.getTaskType() == -1) {
            // 计算延迟时间=结束时间-现在时间-2
            // 下单时间10:30 期望13:00-14:00接单————14:00-10:30>2 14:00-2=12:00 需要延迟12:00-10:30=1.5小时
            LocalDateTime offset = LocalDateTimeUtil.offset(orderMsg.getEstimatedEndTime(), -2, ChronoUnit.HOURS);
            delayTime = Convert.toInt(LocalDateTimeUtil.between(LocalDateTime.now(), offset, ChronoUnit.MILLIS));
        }
        // 8. 发送消息 MqFeign.sendMsg
        this.mqFeign.sendMsg(Constants.MQ.Exchanges.PICKUP_DISPATCH_TASK_DELAYED,
                Constants.MQ.RoutingKeys.PICKUP_DISPATCH_TASK_CREATE, courierTaskMsg.toJson(), delayTime);
    }

    /**
     * 选快递员 筛选出任务最少的快递员的id
     */
    private Long selectCourier(List<Long> courierIds, Integer taskType) {
        // 如果只有一个id，就返回第一个
        if (courierIds.size() == 1) {
            return courierIds.get(0);
        }
        // 查询今日快递员任务数量
        List<CourierTaskCountDTO> countByCourierIds = pickupDispatchTaskFeign.findCountByCourierIds(courierIds, PickupDispatchTaskType.codeOf(taskType),
                DateUtil.date().toDateStr());
        // 如果查询到的快递员数量和参数快递员数量不一致，需要补0
        if (ObjectUtil.notEqual(courierIds.size(), countByCourierIds.size())) {
            // 过滤出 查询到的id列表中 没有的id
            List<CourierTaskCountDTO> collect = courierIds.stream().filter(courierId -> {
                        int index = CollUtil.indexOf(countByCourierIds, dto -> ObjectUtil.equals(courierId, dto.getCourierId()));
                        return index == -1;
                    }).map(courierId -> CourierTaskCountDTO.builder().courierId(courierId).count(0L).build())
                    .collect(Collectors.toList());
            countByCourierIds.addAll(collect);
        }
        // 按照count排序
        CollUtil.sortByProperty(countByCourierIds, "count");
        return countByCourierIds.get(0).getCourierId();
    }

}
